package com.example.util;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Random;
import java.util.concurrent.TimeUnit;

/**
 * @version: V1.0
 * @author: ZhouMengJing
 * @className: RedisLock
 * @packageName: com.project.auth.util
 * @description: redis分布式锁类
 * @data: 2022/9/29 15:52
 **/
@Service
public class RedisLock {
    Logger log = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private RedisClient redisClient;

    public static final String REDIS_LOCK = "RedisLock:";


    /**
     * 10s 锁的超时时间
     */
    private static final long DEFAULT_WAIT_LOCK_TIME_OUT = 10;
    /**
     * 60s锁的有效时间
     */
    private static final long DEFAULT_EXPIRE = 60;

    /**
     * 获取锁
     *
     * @param key
     * @return : boolean
     * @author : ZhouMengJing
     * @date 2022/9/29 15:52
     */
    public boolean lock(String key) {
        return lock(key, DEFAULT_WAIT_LOCK_TIME_OUT, TimeUnit.SECONDS);
    }

    /**
     * 释放锁
     *
     * @param key
     * @return : void
     * @author : ZhouMengJing
     * @date 2022/9/29 15:52
     */
    public void unlock(String key) {
        try {
            String lockKey = generateLockKey(key);
            redisClient.del(lockKey);
        } catch (Exception e) {
            log.error("{}", e.getMessage(), e);
        }
    }

    /**
     * 组装锁的key
     *
     * @param key
     * @return : java.lang.String
     * @author : ZhouMengJing
     * @date 2022/9/29 15:52
     */
    private String generateLockKey(String key) {
        return String.format(REDIS_LOCK + "%s", key);
    }


    /**
     * 获取锁
     * @param key
     * @param timeout
     * @param seconds
     * @return
     */
    public boolean lock(String key, long timeout, TimeUnit seconds) {
        String lockKey = generateLockKey(key);
        long nanoWaitForLock = seconds.toNanos(timeout);
        long start = System.nanoTime();
        try {
            while ((System.nanoTime() - start) < nanoWaitForLock) {
                if (redisClient.setNX(lockKey)) {
                    //暂设置为60s过期，防止异常中断锁未释放
                    redisClient.expire(lockKey, DEFAULT_EXPIRE);
                    if (log.isDebugEnabled()) {
                        log.debug("add RedisLock[{}].{}", key, Thread.currentThread());
                    }
                    return true;
                }
                //加随机时间防止活锁
                TimeUnit.MILLISECONDS.sleep(1000 + new Random().nextInt(100));
            }
        } catch (Exception e) {
            log.error("{}", e.getMessage(), e);
            unlock(key);
        }
        return false;
    }


}